package com.xbrother.lucene.store;

import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.store.*;

import java.io.IOException;
import java.util.Collection;
import java.util.Set;

/**
 * 自定义 Directory 实现，将 Lucene 索引文件存储到数据库中。
 *
 * @author huangchangjin
 * @date 2025/5/14 16:47
 */
@Slf4j
public class DbDirectory extends Directory {
    private final DatabaseIndexStore databaseIndexStore;
    private final LockFactory lockFactory = new SingleInstanceLockFactory();

    public DbDirectory(DatabaseIndexStore databaseIndexStore) {
        this.databaseIndexStore = databaseIndexStore;
    }

    @Override
    public String[] listAll() throws IOException {
        try {
            return databaseIndexStore.listAllFiles();
        } catch (Exception e) {
            log.error("Failed to list files from database", e);
            throw new IOException("Failed to list files from database", e);
        }
    }

    @Override
    public void deleteFile(String name) throws IOException {
        try {
            databaseIndexStore.deleteFile(name);
        } catch (Exception e) {
            log.error("Failed to delete file: {}", name, e);
            throw new IOException("Failed to delete file: " + name, e);
        }
    }

    @Override
    public long fileLength(String name) throws IOException {
        try {
            return databaseIndexStore.getFileLength(name);
        } catch (Exception e) {
            log.error("Failed to get length of file: {}", name, e);
            throw new IOException("Failed to get length of file: " + name, e);
        }
    }

    @Override
    public IndexOutput createOutput(String name, IOContext context) throws IOException {
        try {
            return new DatabaseIndexOutput(name, databaseIndexStore);
        } catch (Exception e) {
            log.error("Failed to create output for file: {}", name, e);
            throw new IOException("Failed to create output for file: " + name, e);
        }
    }

    @Override
    public IndexOutput createTempOutput(String prefix, String suffix, IOContext context) throws IOException {
        String tempFileName = prefix + "_" + System.currentTimeMillis() + suffix;
        if (exists(tempFileName)) {
            throw new IOException("Temporary file already exists: " + tempFileName);
        }
        return createOutput(tempFileName, context);
    }

    private boolean exists(String name) throws IOException {
        try {
            return databaseIndexStore.exists(name);
        } catch (Exception e) {
            log.warn("Error checking existence of file: {}", name, e);
            return false;
        }
    }

    @Override
    public void sync(Collection<String> names) throws IOException {
        // 可选：确保所有指定文件已持久化到数据库
        for (String name : names) {
            databaseIndexStore.syncFile(name);
        }
    }

    @Override
    public void syncMetaData() throws IOException {
        // 可选：同步元数据（如文件列表）
        databaseIndexStore.syncMetadata();
    }

    @Override
    public void rename(String source, String dest) throws IOException {
        try {
            databaseIndexStore.rename(source, dest);
        } catch (Exception e) {
            log.error("Failed to rename file from {} to {}", source, dest, e);
            throw new IOException("Failed to rename file from " + source + " to " + dest, e);
        }
    }

    @Override
    public IndexInput openInput(String fileName, IOContext context) throws IOException {
        try {
            return new DatabaseIndexInput(fileName, databaseIndexStore);
        } catch (Exception e) {
            log.error("Failed to open input for file: {}", fileName, e);
            throw new IOException("Failed to open input for file: " + fileName, e);
        }
    }

    @Override
    public Lock obtainLock(String name) throws IOException {
        return lockFactory.obtainLock(this, name);
    }

    @Override
    public void close() throws IOException {
        // 可选：释放资源
        try {
            databaseIndexStore.close();
        } catch (Exception e) {
            log.warn("Error closing database store", e);
        }
    }

    @Override
    public Set<String> getPendingDeletions() throws IOException {
        try {
            String[] pendingDeletions = databaseIndexStore.getPendingDeletions();
            return pendingDeletions == null ? Set.of() : Set.of(pendingDeletions);
        } catch (Exception e) {
            log.warn("Failed to retrieve pending deletions", e);
            return Set.of();
        }
    }
}
